package ua.org.extjsbuilder.parser.runtime.builder;

import java.io.EOFException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import ua.org.extjsbuilder.parser.IInstruction;
import ua.org.extjsbuilder.parser.runtime.UnparsedToken;
import ua.org.extjsbuilder.parser.runtime.data.JSFunction;
import ua.org.extjsbuilder.parser.runtime.instructions.Declare;
import ua.org.extjsbuilder.parser.runtime.instructions.Empty;
import ua.org.extjsbuilder.parser.runtime.instructions.GetEntity;
import ua.org.extjsbuilder.parser.scanner.CharPattern;

abstract class LValueInterpreter extends AbstractInterpreter implements LValueStates {
	protected static final Map<CharSequence,Integer> KEYWORDS_TO_STATES_MAP;
	
	static {
		Map<CharSequence,Integer> map = new HashMap<CharSequence, Integer>();
		map.put("var", VAR_DECLARATION);
		map.put("function", FUNCTION_DECLARATION);
		
		map.put("if", IF_STATEMENT);
		map.put("switch", SWITCH_STATEMENT);
		map.put("case", CASE_STATEMENT);
		map.put("default", DEFAULT_STATEMENT);
		
		map.put("try", TRY_STATEMENT);
		map.put("catch", CATCH_STATEMENT);
		map.put("finally", FINALLY_STATEMENT);
		
		map.put("for", FOR_CYCLE);
		map.put("while", WHILE_CYCLE);
		map.put("do", DO_CYCLE);
		
		map.put("return", RETURN_STATEMENT);
		map.put("break", BREAK_STATEMENT);
		map.put("continue", CONTINUE_STATEMENT);
		
		map.put("new", NEW_STATEMENT);
		map.put("delete", DELETE_STATEMENT);
		map.put("this", THIS_STATEMENT);
		
		map.put("with", WITH_STATEMENT);
		
		KEYWORDS_TO_STATES_MAP = Collections.unmodifiableMap(map);
	}

	protected IInstruction previous;
	
	@Override
	boolean next() throws EOFException {
		nextChar();
		if(LEFT_END>=state){
			checkForLeftStates();
			checkForLeftEndState();
		}
		if(state==VAR_DECLARATION){
			declareVar();
		}
		if(state==FUNCTION_DECLARATION){
			declareFunction();
		}
		return true;
	}

	protected void declareFunction() throws EOFException{
		skipWhiteSpaces();
		StringBuilder name = new StringBuilder();
		while(JS_INDEFIER.match(data[pos])){
			name.append(data[pos++]);
		}
		skipWhiteSpaces();
		List<IInstruction> arguments = new LinkedList<IInstruction>();
		if(data[pos]!='('){
			err("Open bracket expected");
			while(data[pos]!='{')nextChar();
		}
		else {
			pos++;
			while(nextArgument(arguments));
			pos++;
			skipWhiteSpaces();
			if(data[pos]!='{'){
				err("Block expected");
			}
		}
		GlobalInterpreter interpreter = new GlobalInterpreter(token,pos);
		lastComplete = new JSFunction(name.toString(),
				arguments,parseBlock(interpreter));
		state = LEFT_START;
	}

	private boolean nextArgument(List<IInstruction> arguments) throws EOFException {
		skipWhiteSpaces();
		if(data[pos]==')')return false;
		StringBuilder name = new StringBuilder();
		while(pos<data.length&&JS_INDEFIER.match(data[pos])){
			name.append(data[pos++]);
		}
		if(name.length()==0){
			err("Bad indefier");
			pos++;
			return true;
		}
		skipWhiteSpaces();
		if(data[pos]==',')pos++;
		else if(data[pos]!=')'){
			err("Bad indefier");
			pos++;
		}
		arguments.add(new Declare(name, token.getLine()));
		return true;
	}

	private void declareVar() throws EOFException {
		char c = data[pos];
		do {
			c = nextChar();
		}
		while(CharPattern.ALLOW_WHITE_SPACES.match(c));
		buf = new StringBuilder();
		while(pos<data.length&&JS_INDEFIER.match(c)) {
			buf.append(c);
			pos++;
			if(pos<data.length){
				c = data[pos];
			}
		}
		if(buf.length()==0){
			err("Indefier expected",token.getLine());
		}
		if(JS_KEYWORDS.matcher(buf).matches()){
			err("Indefier "+buf+" is reserved",token.getLine());
		}
		if(isInstructionEnd()){
			state = LEFT_START;
			lastComplete = new Declare(buf,token.getLine());
		}
		else {
			previous = new Declare(buf,token.getLine());
			state = EXPECT_OPERATOR;
		}
	}

	LValueInterpreter(UnparsedToken start) {
		super(start);
	}
	
	private void checkForLeftStates() throws EOFException {
		if(state==LEFT_START){
			char c = data[pos];
			if(CharPattern.ALLOW_WHITE_SPACES.match(c)){
				return;
			}
			buf = new StringBuilder();
			previous = null;
			buf.append(c);
			state = LEFT_INSIDE;
		}
		else if(state==LEFT_INSIDE){
			char c = data[pos];
			if(!JS_INDEFIER.match(c)){
				state = LEFT_END;
			}
			else {
				buf.append(c);
			}
		}
	}

	protected void checkForLeftEndState() throws EOFException {
		if(pos>=(data.length-1)){
			state = LEFT_END;
		}
		if(state==LEFT_END){
			analyzeKeyword();
		}
	}
	
	protected void analyzeKeyword() throws EOFException {
		String str = buf.toString();
		if(str.length()==0){
			lastComplete = new Empty();
			state = LEFT_START;
			return;
		}
		if(KEYWORDS_TO_STATES_MAP.containsKey(str)){
			state = KEYWORDS_TO_STATES_MAP.get(str);
			return;
		}
		previous = new GetEntity(str);
		if(!isInstructionEnd()){
			state = EXPECT_OPERATOR;
		}
	}

	@Override
	protected void setState(AbstractInterpreter interpreter, boolean deep) {
		super.setState(interpreter, deep);
		if((interpreter instanceof LValueInterpreter)
				&&deep){
			previous = ((LValueInterpreter)interpreter).previous;
		}
	}
}
